// step.cpp
// Copyright (C) 2008 Sam Bateman
// samuel.bateman@gmail.com
//
// This file is part of DVD On Fire.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.

#include <string>
#include <libdvdonfire/thread.h>
#include "data.h"
#include "step.h"
using namespace dvdonfire::lib;
using namespace std;

namespace dvdonfire
{
    namespace daemon
    {
        // struct Step::Impl {{{
        struct Step::Impl
        {
            Data   *data;
            bool    done;
            string  error;
            string  name;
            float   progress;
            Thread *thread;
        };
        // }}}
        // Step::Step(name) {{{
        //! @brief Constructor.
        //! @param name Name of the step.
        Step::Step(const std::string& name)
        {
            impl = new Impl;
            impl->data = NULL;
            impl->done = false;
            impl->error = "";
            impl->name = name;
            impl->progress = 0;
            impl->thread = new Thread(this);
        }
        // }}}
        // Step::~Step() {{{
        //! @brief Destructor.
        Step::~Step()
        {
            delete impl->thread;
            delete impl;
        }
        // }}}
        // Step::getData() {{{
        //! @brief Returns global data.
        //! @return Instance of Data.
        Data* Step::getData()
        {
            return impl->data;
        }
        // }}}
        // Step::getError() {{{
        //! @brief Returns the error.
        //! @return Error message, or empty string if no error has occurred.
        string Step::getError() const
        {
            return impl->error;
        }
        // }}}
        // Step::getProgress() {{{
        //! @brief Returns progress.
        //! @return Progress of the step so far.
        float Step::getProgress() const
        {
            return impl->progress;
        }
        // }}}
        // Step::isDone() {{{
        //! @brief Returns finished status.
        //! @return Whether the step has finished.
        bool Step::isDone() const
        {
            return impl->done;
        }
        // }}}
        // Step::logArgs(args, num_args) {{{
        //! @brief Logs process arguments to file.
        //! @param args Arguments to log.
        //! @param num_args Length of the args array.
        //!
        //! Provides a convenient way to log the command-line arguments given
        //! the Process constructor.
        void Step::logArgs(const std::string *args, size_t num_args)
        {
            string str = "Spawning \"";
            for (size_t i = 0; i < num_args; ++i){
                if (i != 0) str += " ";
                if (args[i].find(' ') != string::npos){
                    str += "'" + args[i] + "'";
                }
                else {
                    str += args[i];
                }
            }
            str += "\"";
            logInfo(str);
        }
        // }}}
        // Step::logDebug(message) {{{
        //! @brief Logs debug messages.
        //! @param message Message to log.
        //!
        //! Convenience function for logging messages with DEBUG severity.
        void Step::logDebug(const std::string& message)
        {
            impl->data->getLog()->log(impl->name, message, Log::DEBUG);
        }
        // }}}
        // Step::logError(message) {{{
        //! @brief Logs error messages.
        //! @param message Message to log.
        //!
        //! Convenience function for logging messages with ERROR severity.
        void Step::logError(const std::string& message)
        {
            impl->data->getLog()->log(impl->name, message, Log::ERROR);
        }
        // }}}
        // Step::logInfo(message) {{{
        //! @brief Logs info messages.
        //! @param message Message to log.
        //!
        //! Convenience function for logging messages with INFO severity.
        void Step::logInfo(const std::string& message)
        {
            impl->data->getLog()->log(impl->name, message, Log::INFO);
        }
        // }}}
        // Step::logWarn(message) {{{
        //! @brief Logs warn messages.
        //! @param message Message to log.
        //!
        //! Convenience function for logging messages with WARN severity.
        void Step::logWarn(const std::string& message)
        {
            impl->data->getLog()->log(impl->name, message, Log::WARNING);
        }
        // }}}
        // Step::run() {{{
        //! @brief Runs the step.
        //!
        //! Runs the step in a separate thread.  This function is basically a
        //! wrapper around doStep() to handle throwing StepErrors.  Do not
        //! call this, call start() instead.
        void Step::run()
        {
            try {
                doStep();
            }
            catch (const StepError& se){
                impl->error = se.what();
                logError(impl->error);
            }
            impl->done = true;
        }
        // }}}
        // Step::setData(data) {{{
        //! @brief Sets global data.
        //! @param data Instance of Data.
        void Step::setData(Data *data)
        {
            impl->data = data;
        }
        // }}}
        // Step::setProgress(progress) {{{
        //! @brief Sets step progress.
        //! @param progress Step progress so far.
        void Step::setProgress(float progress)
        {
            impl->progress = progress;
        }
        // }}}
        // Step::start() {{{
        //! @brief Starts the step.
        //!
        //! Starts running the step in a separate thread.  This function
        //! returns immediately after creating the new thread.
        void Step::start()
        {
            impl->thread->start();
        }
        // }}}
        // Step::stop() {{{
        //! @brief Stops the step.
        //!
        //! Interrupt the thread that the step is running in.
        void Step::stop()
        {
            impl->thread->stop();
        }
        // }}}
        // Step::yield() {{{
        //! @brief Yields the processor.
        //! @return Whether the thread has been interrupted.
        //!
        //! This is a wrapper around Thread::yield().
        bool Step::yield()
        {
            return impl->thread->yield();
        }
        // }}}
    }
}
